package com.dbflow5.query;

import com.dbflow5.StringUtils;
import com.dbflow5.query.property.IProperty;
import com.dbflow5.query.property.Property;
import com.dbflow5.sql.Query;

import java.util.ArrayList;
import java.util.List;

/**
 * Description: Represents a SQLITE CASE argument.
 */
public class Case<TReturn> implements Query {
    private IProperty<?> caseColumn = null;
    private final List<CaseCondition<TReturn>> caseConditions = new ArrayList<>();
    private String columnName = null;
    private TReturn elseValue = null;
    private boolean elseSpecified;

    // when true, only WHEN value is supported. Not WHEN condition
    public boolean isEfficientCase;

    private boolean endSpecified;

    public Case(IProperty<?> caseColumn) {
        this.caseColumn = caseColumn;
        if (this.caseColumn != null) {
            isEfficientCase = true;
        }
    }

    @Override
    public String getQuery() {
        StringBuilder queryBuilder = new StringBuilder(" CASE");
        if (isEfficientCase) {
            queryBuilder.append(" ");
            queryBuilder.append(BaseOperator.convertValueToString(caseColumn, false));
        }

        StringBuilder builder = new StringBuilder();
        for(CaseCondition<TReturn> caseCondition : caseConditions) {
            builder.append(caseCondition.toString());
        }
        queryBuilder.append(builder.toString());

        if (elseSpecified) {
            queryBuilder.append(" ELSE ")
                    .append(BaseOperator.convertValueToString(elseValue, false));
        }
        if (endSpecified) {
            queryBuilder.append(" END ");
            queryBuilder.append(columnName != null? columnName : "");
        }
        return queryBuilder.toString();
    }

    public Case() {
        this(null);
    }

    //@JvmName("when")
    public CaseCondition<TReturn> whenever(SQLOperator sqlOperator) {
        if (isEfficientCase) {
            throw new IllegalStateException("When using the efficient CASE method,"
                + "you must pass in value only, not condition.");
        }
        CaseCondition<TReturn> caseCondition = new CaseCondition<>(this, sqlOperator);
        caseConditions.add(caseCondition);
        return caseCondition;
    }

    //@JvmName("when")
    public CaseCondition<TReturn> whenever(TReturn whenValue) {
        if (!isEfficientCase) {
            throw new IllegalStateException("When not using the efficient CASE method, "
                + "you must pass in the SQLOperator as a parameter");
        }
        CaseCondition<TReturn> caseCondition = new CaseCondition<>(this, whenValue);
        caseConditions.add(caseCondition);
        return caseCondition;
    }

    //@JvmName("when")
    public CaseCondition<TReturn> whenever(IProperty<?> property) {
        if (!isEfficientCase) {
            throw new IllegalStateException("When not using the efficient CASE method, "
                + "you must pass in the SQLOperator as a parameter");
        }
        CaseCondition<TReturn> caseCondition = new CaseCondition<>(this, property);
        caseConditions.add(caseCondition);
        return caseCondition;
    }

    /**
     * Default case here. If not specified, value will be NULL.
     *
     * @param elseValue elseValue
     * @return this
     */
    public Case<TReturn> _else(TReturn elseValue) {
        this.elseValue = elseValue;
        elseSpecified = true; // ensure its set especially if null specified.
        return this;
    }

    /**
     * Property
     *
     * @param columnName The name of the case that we return in a column.
     * @return The case completed as a property.
     */
    public Property<Case<TReturn>> end(String columnName) {
        endSpecified = true;
        if (columnName != null) {
            this.columnName = StringUtils.quoteIfNeeded(columnName);
        }
        return new Property(null, NameAlias.rawBuilder(getQuery()).build());
    }

    /**
     * endAsOperator
     *
     * @param <T> T
     * @return The case complete as an operator.
     */
    public <T> Operator<T> endAsOperator() {
        return Operator.op(end(null).nameAlias());
    }

}


